<!DOCTYPE html>
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
  <meta name="description" content="刘清政">
  <meta name="keyword" content="hexo-theme">
  
    <link rel="shortcut icon" href="/css/images/logo.png">
  
  <title>
    
      Go语言之context | Justin-刘清政的博客
    
  </title>
  <link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <link href="//cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.css" rel="stylesheet">
  <link href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/tomorrow.min.css" rel="stylesheet">
  
<link rel="stylesheet" href="/css/style.css">

  
    
<link rel="stylesheet" href="/css/plugins/gitment.css">

  
  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/geopattern/1.2.3/js/geopattern.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.js"></script>
  
    
<script src="/js/qrious.js"></script>

  
  
    
<script src="/js/gitment.js"></script>

  
  

  
<meta name="generator" content="Hexo 4.2.0"></head>
<div class="wechat-share">
  <img src="/css/images/logo.png" />
</div>

  <body>
    <header class="header fixed-header">
  <div class="header-container">
    <a class="home-link" href="/">
      <div class="logo"></div>
      <span>Justin-刘清政的博客</span>
    </a>
    <ul class="right-list">
      
        <li class="list-item">
          
            <a href="/" class="item-link">主页</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/tags/" class="item-link">标签</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/archives/" class="item-link">归档</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/about/" class="item-link">关于我</a>
          
        </li>
      
    </ul>
    <div class="menu">
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
    </div>
    <div class="menu-mask">
      <ul class="menu-list">
        
          <li class="menu-item">
            
              <a href="/" class="menu-link">主页</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/tags/" class="menu-link">标签</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/archives/" class="menu-link">归档</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/about/" class="menu-link">关于我</a>
            
          </li>
        
      </ul>
    </div>
  </div>
</header>

    <div id="article-banner">
  <h2>Go语言之context</h2>



  <p class="post-date">2019-03-10</p>
    <!-- 不蒜子统计 -->
    <span id="busuanzi_container_page_pv" style='display:none' class="">
        <i class="icon-smile icon"></i> 阅读数：<span id="busuanzi_value_page_pv"></span>次
    </span>
  <div class="arrow-down">
    <a href="javascript:;"></a>
  </div>
</div>
<main class="app-body flex-box">
  <!-- Article START -->
  <article class="post-article">
    <section class="markdown-content"><p>在 Go http包的Server中，每一个请求在都有一个对应的 goroutine 去处理。请求处理函数通常会启动额外的 goroutine 用来访问后端服务，比如数据库和RPC服务。用来处理一个请求的 goroutine 通常需要访问一些与请求特定的数据，比如终端用户的身份认证信息、验证相关的token、请求的截止时间。 当一个请求被取消或超时时，所有用来处理该请求的 goroutine 都应该迅速退出，然后系统才能释放这些 goroutine 占用的资源。</p>

<h1 id="为什么需要Context"><a href="#为什么需要Context" class="headerlink" title="为什么需要Context"></a>为什么需要Context</h1><h1 id="基本示例"><a href="#基本示例" class="headerlink" title="基本示例"></a>基本示例</h1><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	&amp;quot;fmt&amp;quot;</span><br><span class="line">	&amp;quot;sync&amp;quot;</span><br><span class="line"></span><br><span class="line">	&amp;quot;time&amp;quot;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始的例子</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">worker</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="keyword">for</span> &#123;</span><br><span class="line">		fmt.Println(&amp;quot;worker&amp;quot;)</span><br><span class="line">		time.Sleep(time.Second)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">// 如何接收外部命令实现退出</span></span><br><span class="line">	wg.Done()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	wg.Add(<span class="number">1</span>)</span><br><span class="line">	<span class="keyword">go</span> worker()</span><br><span class="line">	<span class="comment">// 如何优雅的实现结束子goroutine</span></span><br><span class="line">	wg.Wait()</span><br><span class="line">	fmt.Println(&amp;quot;over&amp;quot;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="全局变量方式"><a href="#全局变量方式" class="headerlink" title="全局变量方式"></a>全局变量方式</h1><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	&amp;quot;fmt&amp;quot;</span><br><span class="line">	&amp;quot;sync&amp;quot;</span><br><span class="line"></span><br><span class="line">	&amp;quot;time&amp;quot;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"><span class="keyword">var</span> exit <span class="keyword">bool</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 全局变量方式存在的问题：</span></span><br><span class="line"><span class="comment">// 1. 使用全局变量在跨包调用时不容易统一</span></span><br><span class="line"><span class="comment">// 2. 如果worker中再启动goroutine，就不太好控制了。</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">worker</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="keyword">for</span> &#123;</span><br><span class="line">		fmt.Println(&amp;quot;worker&amp;quot;)</span><br><span class="line">		time.Sleep(time.Second)</span><br><span class="line">		<span class="keyword">if</span> exit &#123;</span><br><span class="line">			<span class="keyword">break</span></span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	wg.Done()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	wg.Add(<span class="number">1</span>)</span><br><span class="line">	<span class="keyword">go</span> worker()</span><br><span class="line">	time.Sleep(time.Second * <span class="number">3</span>) <span class="comment">// sleep3秒以免程序过快退出</span></span><br><span class="line">	exit = <span class="literal">true</span>                 <span class="comment">// 修改全局变量实现子goroutine的退出</span></span><br><span class="line">	wg.Wait()</span><br><span class="line">	fmt.Println(&amp;quot;over&amp;quot;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="通道方式"><a href="#通道方式" class="headerlink" title="通道方式"></a>通道方式</h1><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	&amp;quot;fmt&amp;quot;</span><br><span class="line">	&amp;quot;sync&amp;quot;</span><br><span class="line"></span><br><span class="line">	&amp;quot;time&amp;quot;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"></span><br><span class="line"><span class="comment">// 管道方式存在的问题：</span></span><br><span class="line"><span class="comment">// 1. 使用全局变量在跨包调用时不容易实现规范和统一，需要维护一个共用的channel</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">worker</span><span class="params">(exitChan <span class="keyword">chan</span> <span class="keyword">struct</span>&#123;&#125;)</span></span> &#123;</span><br><span class="line">LOOP:</span><br><span class="line">	<span class="keyword">for</span> &#123;</span><br><span class="line">		fmt.Println(&amp;quot;worker&amp;quot;)</span><br><span class="line">		time.Sleep(time.Second)</span><br><span class="line">		<span class="keyword">select</span> &#123;</span><br><span class="line">		<span class="keyword">case</span> &amp;lt;-exitChan: <span class="comment">// 等待接收上级通知</span></span><br><span class="line">			<span class="keyword">break</span> LOOP</span><br><span class="line">		<span class="keyword">default</span>:</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	wg.Done()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="keyword">var</span> exitChan = <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">struct</span>&#123;&#125;)</span><br><span class="line">	wg.Add(<span class="number">1</span>)</span><br><span class="line">	<span class="keyword">go</span> worker(exitChan)</span><br><span class="line">	time.Sleep(time.Second * <span class="number">3</span>) <span class="comment">// sleep3秒以免程序过快退出</span></span><br><span class="line">	exitChan &amp;lt;- <span class="keyword">struct</span>&#123;&#125;&#123;&#125;      <span class="comment">// 给子goroutine发送退出信号</span></span><br><span class="line">	<span class="built_in">close</span>(exitChan)</span><br><span class="line">	wg.Wait()</span><br><span class="line">	fmt.Println(&amp;quot;over&amp;quot;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="官方版的方案"><a href="#官方版的方案" class="headerlink" title="官方版的方案"></a>官方版的方案</h1><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	&amp;quot;context&amp;quot;</span><br><span class="line">	&amp;quot;fmt&amp;quot;</span><br><span class="line">	&amp;quot;sync&amp;quot;</span><br><span class="line"></span><br><span class="line">	&amp;quot;time&amp;quot;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">worker</span><span class="params">(ctx context.Context)</span></span> &#123;</span><br><span class="line">LOOP:</span><br><span class="line">	<span class="keyword">for</span> &#123;</span><br><span class="line">		fmt.Println(&amp;quot;worker&amp;quot;)</span><br><span class="line">		time.Sleep(time.Second)</span><br><span class="line">		<span class="keyword">select</span> &#123;</span><br><span class="line">		<span class="keyword">case</span> &amp;lt;-ctx.Done(): <span class="comment">// 等待上级通知</span></span><br><span class="line">			<span class="keyword">break</span> LOOP</span><br><span class="line">		<span class="keyword">default</span>:</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	wg.Done()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	ctx, cancel := context.WithCancel(context.Background())</span><br><span class="line">	wg.Add(<span class="number">1</span>)</span><br><span class="line">	<span class="keyword">go</span> worker(ctx)</span><br><span class="line">	time.Sleep(time.Second * <span class="number">3</span>)</span><br><span class="line">	cancel() <span class="comment">// 通知子goroutine结束</span></span><br><span class="line">	wg.Wait()</span><br><span class="line">	fmt.Println(&amp;quot;over&amp;quot;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当子goroutine又开启另外一个goroutine时，只需要将ctx传入即可：</p>

<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	&amp;quot;context&amp;quot;</span><br><span class="line">	&amp;quot;fmt&amp;quot;</span><br><span class="line">	&amp;quot;sync&amp;quot;</span><br><span class="line"></span><br><span class="line">	&amp;quot;time&amp;quot;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">worker</span><span class="params">(ctx context.Context)</span></span> &#123;</span><br><span class="line">	<span class="keyword">go</span> worker2(ctx)</span><br><span class="line">LOOP:</span><br><span class="line">	<span class="keyword">for</span> &#123;</span><br><span class="line">		fmt.Println(&amp;quot;worker&amp;quot;)</span><br><span class="line">		time.Sleep(time.Second)</span><br><span class="line">		<span class="keyword">select</span> &#123;</span><br><span class="line">		<span class="keyword">case</span> &amp;lt;-ctx.Done(): <span class="comment">// 等待上级通知</span></span><br><span class="line">			<span class="keyword">break</span> LOOP</span><br><span class="line">		<span class="keyword">default</span>:</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	wg.Done()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">worker2</span><span class="params">(ctx context.Context)</span></span> &#123;</span><br><span class="line">LOOP:</span><br><span class="line">	<span class="keyword">for</span> &#123;</span><br><span class="line">		fmt.Println(&amp;quot;worker2&amp;quot;)</span><br><span class="line">		time.Sleep(time.Second)</span><br><span class="line">		<span class="keyword">select</span> &#123;</span><br><span class="line">		<span class="keyword">case</span> &amp;lt;-ctx.Done(): <span class="comment">// 等待上级通知</span></span><br><span class="line">			<span class="keyword">break</span> LOOP</span><br><span class="line">		<span class="keyword">default</span>:</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	ctx, cancel := context.WithCancel(context.Background())</span><br><span class="line">	wg.Add(<span class="number">1</span>)</span><br><span class="line">	<span class="keyword">go</span> worker(ctx)</span><br><span class="line">	time.Sleep(time.Second * <span class="number">3</span>)</span><br><span class="line">	cancel() <span class="comment">// 通知子goroutine结束</span></span><br><span class="line">	wg.Wait()</span><br><span class="line">	fmt.Println(&amp;quot;over&amp;quot;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="Context初识"><a href="#Context初识" class="headerlink" title="Context初识"></a>Context初识</h1><p>Go1.7加入了一个新的标准库<code>context</code>，它定义了<code>Context</code>类型，专门用来简化 对于处理单个请求的多个 goroutine 之间与请求域的数据、取消信号、截止时间等相关操作，这些操作可能涉及多个 API 调用。</p>

<p>对服务器传入的请求应该创建上下文，而对服务器的传出调用应该接受上下文。它们之间的函数调用链必须传递上下文，或者可以使用<code>WithCancel</code>、<code>WithDeadline</code>、<code>WithTimeout</code>或<code>WithValue</code>创建的派生上下文。当一个上下文被取消时，它派生的所有上下文也被取消。</p>

<h1 id="Context接口"><a href="#Context接口" class="headerlink" title="Context接口"></a>Context接口</h1><p><code>context.Context</code>是一个接口，该接口定义了四个需要实现的方法。具体签名如下：</p>

<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Context <span class="keyword">interface</span> &#123;</span><br><span class="line">    Deadline() (deadline time.Time, ok <span class="keyword">bool</span>)</span><br><span class="line">    Done() &amp;lt;-<span class="keyword">chan</span> <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line">    Err() error</span><br><span class="line">    Value(key <span class="keyword">interface</span>&#123;&#125;) <span class="keyword">interface</span>&#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其中：</p>

<ul>
<li><code>Deadline</code>方法需要返回当前<code>Context</code>被取消的时间，也就是完成工作的截止时间（deadline）；</li>
<li><code>Done</code>方法需要返回一个<code>Channel</code>，这个Channel会在当前工作完成或者上下文被取消之后关闭，多次调用<code>Done</code>方法会返回同一个Channel；</li>
<li><code>Err</code>方法会返回当前<code>Context</code>结束的原因，它只会在<code>Done</code>返回的Channel被关闭时才会返回非空的值；

<ul>
<li>如果当前<code>Context</code>被取消就会返回<code>Canceled</code>错误；</li>
<li>如果当前<code>Context</code>超时就会返回<code>DeadlineExceeded</code>错误；</li>
</ul></li>
<li><code>Value</code>方法会从<code>Context</code>中返回键对应的值，对于同一个上下文来说，多次调用<code>Value</code> 并传入相同的<code>Key</code>会返回相同的结果，该方法仅用于传递跨API和进程间跟请求域的数据；</li>
</ul>

<h1 id="Background-和TODO"><a href="#Background-和TODO" class="headerlink" title="Background()和TODO()"></a>Background()和TODO()</h1><p>Go内置两个函数：<code>Background()</code>和<code>TODO()</code>，这两个函数分别返回一个实现了<code>Context</code>接口的<code>background</code>和<code>todo</code>。我们代码中最开始都是以这两个内置的上下文对象作为最顶层的<code>partent context</code>，衍生出更多的子上下文对象。</p>

<p><code>Background()</code>主要用于main函数、初始化以及测试代码中，作为Context这个树结构的最顶层的Context，也就是根Context。</p>

<p><code>TODO()</code>，它目前还不知道具体的使用场景，如果我们不知道该使用什么Context的时候，可以使用这个。</p>

<p><code>background</code>和<code>todo</code>本质上都是<code>emptyCtx</code>结构体类型，是一个不可取消，没有设置截止时间，没有携带任何值的Context。</p>

<h1 id="With系列函数"><a href="#With系列函数" class="headerlink" title="With系列函数"></a>With系列函数</h1><p>此外，<code>context</code>包中还定义了四个With系列函数。</p>

<h1 id="WithCancel"><a href="#WithCancel" class="headerlink" title="WithCancel"></a>WithCancel</h1><p><code>WithCancel</code>的函数签名如下：</p>

<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">WithCancel</span><span class="params">(parent Context)</span> <span class="params">(ctx Context, cancel CancelFunc)</span></span></span><br></pre></td></tr></table></figure>

<p><code>WithCancel</code>返回带有新Done通道的父节点的副本。当调用返回的cancel函数或当关闭父上下文的Done通道时，将关闭返回上下文的Done通道，无论先发生什么情况。</p>

<p>取消此上下文将释放与其关联的资源，因此代码应该在此上下文中运行的操作完成后立即调用cancel。</p>

<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">gen</span><span class="params">(ctx context.Context)</span> &amp;<span class="title">lt</span>;-<span class="title">chan</span> <span class="title">int</span></span> &#123;</span><br><span class="line">		dst := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">		n := <span class="number">1</span></span><br><span class="line">		<span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">			<span class="keyword">for</span> &#123;</span><br><span class="line">				<span class="keyword">select</span> &#123;</span><br><span class="line">				<span class="keyword">case</span> &amp;lt;-ctx.Done():</span><br><span class="line">					<span class="keyword">return</span> <span class="comment">// return结束该goroutine，防止泄露</span></span><br><span class="line">				<span class="keyword">case</span> dst &amp;lt;- n:</span><br><span class="line">					n++</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;()</span><br><span class="line">		<span class="keyword">return</span> dst</span><br><span class="line">	&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	ctx, cancel := context.WithCancel(context.Background())</span><br><span class="line">	<span class="keyword">defer</span> cancel() <span class="comment">// 当我们取完需要的整数后调用cancel</span></span><br><span class="line"></span><br><span class="line">	<span class="keyword">for</span> n := <span class="keyword">range</span> gen(ctx) &#123;</span><br><span class="line">		fmt.Println(n)</span><br><span class="line">		<span class="keyword">if</span> n == <span class="number">5</span> &#123;</span><br><span class="line">			<span class="keyword">break</span></span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的示例代码中，<code>gen</code>函数在单独的goroutine中生成整数并将它们发送到返回的通道。
gen的调用者在使用生成的整数之后需要取消上下文，以免<code>gen</code>启动的内部goroutine发生泄漏。</p>

<h1 id="WithDeadline"><a href="#WithDeadline" class="headerlink" title="WithDeadline"></a>WithDeadline</h1><p><code>WithDeadline</code>的函数签名如下：</p>

<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">WithDeadline</span><span class="params">(parent Context, deadline time.Time)</span> <span class="params">(Context, CancelFunc)</span></span></span><br></pre></td></tr></table></figure>

<p>返回父上下文的副本，并将deadline调整为不迟于d。如果父上下文的deadline已经早于d，则WithDeadline(parent, d)在语义上等同于父上下文。当截止日过期时，当调用返回的cancel函数时，或者当父上下文的Done通道关闭时，返回上下文的Done通道将被关闭，以最先发生的情况为准。</p>

<p>取消此上下文将释放与其关联的资源，因此代码应该在此上下文中运行的操作完成后立即调用cancel。</p>

<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	d := time.Now().Add(<span class="number">50</span> * time.Millisecond)</span><br><span class="line">	ctx, cancel := context.WithDeadline(context.Background(), d)</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 尽管ctx会过期，但在任何情况下调用它的cancel函数都是很好的实践。</span></span><br><span class="line">	<span class="comment">// 如果不这样做，可能会使上下文及其父类存活的时间超过必要的时间。</span></span><br><span class="line">	<span class="keyword">defer</span> cancel()</span><br><span class="line"></span><br><span class="line">	<span class="keyword">select</span> &#123;</span><br><span class="line">	<span class="keyword">case</span> &amp;lt;-time.After(<span class="number">1</span> * time.Second):</span><br><span class="line">		fmt.Println(&amp;quot;overslept&amp;quot;)</span><br><span class="line">	<span class="keyword">case</span> &amp;lt;-ctx.Done():</span><br><span class="line">		fmt.Println(ctx.Err())</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的代码中，定义了一个50毫秒之后过期的deadline，然后我们调用<code>context.WithDeadline(context.Background(), d)</code>得到一个上下文（ctx）和一个取消函数（cancel），然后使用一个select让主程序陷入等待：等待1秒后打印<code>overslept</code>退出或者等待ctx过期后退出。
因为ctx50秒后就过期，所以<code>ctx.Done()</code>会先接收到值，上面的代码会打印ctx.Err()取消原因。</p>

<h1 id="WithTimeout"><a href="#WithTimeout" class="headerlink" title="WithTimeout"></a>WithTimeout</h1><p><code>WithTimeout</code>的函数签名如下：</p>

<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">WithTimeout</span><span class="params">(parent Context, timeout time.Duration)</span> <span class="params">(Context, CancelFunc)</span></span></span><br></pre></td></tr></table></figure>

<p><code>WithTimeout</code>返回<code>WithDeadline(parent, time.Now().Add(timeout))</code>。</p>

<p>取消此上下文将释放与其相关的资源，因此代码应该在此上下文中运行的操作完成后立即调用cancel，通常用于数据库或者网络连接的超时控制。具体示例如下：</p>

<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	&amp;quot;context&amp;quot;</span><br><span class="line">	&amp;quot;fmt&amp;quot;</span><br><span class="line">	&amp;quot;sync&amp;quot;</span><br><span class="line"></span><br><span class="line">	&amp;quot;time&amp;quot;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// context.WithTimeout</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">worker</span><span class="params">(ctx context.Context)</span></span> &#123;</span><br><span class="line">LOOP:</span><br><span class="line">	<span class="keyword">for</span> &#123;</span><br><span class="line">		fmt.Println(&amp;quot;db connecting ...&amp;quot;)</span><br><span class="line">		time.Sleep(time.Millisecond * <span class="number">10</span>) <span class="comment">// 假设正常连接数据库耗时10毫秒</span></span><br><span class="line">		<span class="keyword">select</span> &#123;</span><br><span class="line">		<span class="keyword">case</span> &amp;lt;-ctx.Done(): <span class="comment">// 50毫秒后自动调用</span></span><br><span class="line">			<span class="keyword">break</span> LOOP</span><br><span class="line">		<span class="keyword">default</span>:</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	fmt.Println(&amp;quot;worker done!&amp;quot;)</span><br><span class="line">	wg.Done()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="comment">// 设置一个50毫秒的超时</span></span><br><span class="line">	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*<span class="number">50</span>)</span><br><span class="line">	wg.Add(<span class="number">1</span>)</span><br><span class="line">	<span class="keyword">go</span> worker(ctx)</span><br><span class="line">	time.Sleep(time.Second * <span class="number">5</span>)</span><br><span class="line">	cancel() <span class="comment">// 通知子goroutine结束</span></span><br><span class="line">	wg.Wait()</span><br><span class="line">	fmt.Println(&amp;quot;over&amp;quot;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="WithValue"><a href="#WithValue" class="headerlink" title="WithValue"></a>WithValue</h1><p><code>WithValue</code>函数能够将请求作用域的数据与 Context 对象建立关系。声明如下：</p>

<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">WithValue</span><span class="params">(parent Context, key, val <span class="keyword">interface</span>&#123;&#125;)</span> <span class="title">Context</span></span></span><br></pre></td></tr></table></figure>

<p><code>WithValue</code>返回父节点的副本，其中与key关联的值为val。</p>

<p>仅对API和进程间传递请求域的数据使用上下文值，而不是使用它来传递可选参数给函数。</p>

<p>所提供的键必须是可比较的，并且不应该是<code>string</code>类型或任何其他内置类型，以避免使用上下文在包之间发生冲突。<code>WithValue</code>的用户应该为键定义自己的类型。为了避免在分配给interface{}时进行分配，上下文键通常具有具体类型<code>struct{}</code>。或者，导出的上下文关键变量的静态类型应该是指针或接口。</p>

<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	&amp;quot;context&amp;quot;</span><br><span class="line">	&amp;quot;fmt&amp;quot;</span><br><span class="line">	&amp;quot;sync&amp;quot;</span><br><span class="line"></span><br><span class="line">	&amp;quot;time&amp;quot;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// context.WithValue</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> TraceCode <span class="keyword">string</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">worker</span><span class="params">(ctx context.Context)</span></span> &#123;</span><br><span class="line">	key := TraceCode(&amp;quot;TRACE_CODE&amp;quot;)</span><br><span class="line">	traceCode, ok := ctx.Value(key).(<span class="keyword">string</span>) <span class="comment">// 在子goroutine中获取trace code</span></span><br><span class="line">	<span class="keyword">if</span> !ok &#123;</span><br><span class="line">		fmt.Println(&amp;quot;invalid trace code&amp;quot;)</span><br><span class="line">	&#125;</span><br><span class="line">LOOP:</span><br><span class="line">	<span class="keyword">for</span> &#123;</span><br><span class="line">		fmt.Printf(&amp;quot;worker, trace code:%s\n&amp;quot;, traceCode)</span><br><span class="line">		time.Sleep(time.Millisecond * <span class="number">10</span>) <span class="comment">// 假设正常连接数据库耗时10毫秒</span></span><br><span class="line">		<span class="keyword">select</span> &#123;</span><br><span class="line">		<span class="keyword">case</span> &amp;lt;-ctx.Done(): <span class="comment">// 50毫秒后自动调用</span></span><br><span class="line">			<span class="keyword">break</span> LOOP</span><br><span class="line">		<span class="keyword">default</span>:</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	fmt.Println(&amp;quot;worker done!&amp;quot;)</span><br><span class="line">	wg.Done()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="comment">// 设置一个50毫秒的超时</span></span><br><span class="line">	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*<span class="number">50</span>)</span><br><span class="line">	<span class="comment">// 在系统的入口中设置trace code传递给后续启动的goroutine实现日志数据聚合</span></span><br><span class="line">	ctx = context.WithValue(ctx, TraceCode(&amp;quot;TRACE_CODE&amp;quot;), &amp;quot;<span class="number">12512312234</span>&amp;quot;)</span><br><span class="line">	wg.Add(<span class="number">1</span>)</span><br><span class="line">	<span class="keyword">go</span> worker(ctx)</span><br><span class="line">	time.Sleep(time.Second * <span class="number">5</span>)</span><br><span class="line">	cancel() <span class="comment">// 通知子goroutine结束</span></span><br><span class="line">	wg.Wait()</span><br><span class="line">	fmt.Println(&amp;quot;over&amp;quot;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="使用Context的注意事项"><a href="#使用Context的注意事项" class="headerlink" title="使用Context的注意事项"></a>使用Context的注意事项</h1><ul>
<li>推荐以参数的方式显示传递Context</li>
<li>以Context作为参数的函数方法，应该把Context作为第一个参数。</li>
<li>给一个函数方法传递Context的时候，不要传递nil，如果不知道传递什么，就使用context.TODO()</li>
<li>Context的Value相关方法应该传递请求域的必要数据，不应该用于传递可选参数</li>
<li>Context是线程安全的，可以放心的在多个goroutine中传递</li>
</ul>

<h1 id="客户端超时取消示例"><a href="#客户端超时取消示例" class="headerlink" title="客户端超时取消示例"></a>客户端超时取消示例</h1><p>调用服务端API时如何在客户端实现超时控制？</p>

<h1 id="server端"><a href="#server端" class="headerlink" title="server端"></a>server端</h1><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// context_timeout/server/main.go</span></span><br><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	&amp;quot;fmt&amp;quot;</span><br><span class="line">	&amp;quot;math/rand&amp;quot;</span><br><span class="line">	&amp;quot;net/http&amp;quot;</span><br><span class="line"></span><br><span class="line">	&amp;quot;time&amp;quot;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// server端，随机出现慢响应</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">indexHandler</span><span class="params">(w http.ResponseWriter, r *http.Request)</span></span> &#123;</span><br><span class="line">	number := rand.Intn(<span class="number">2</span>)</span><br><span class="line">	<span class="keyword">if</span> number == <span class="number">0</span> &#123;</span><br><span class="line">		time.Sleep(time.Second * <span class="number">10</span>) <span class="comment">// 耗时10秒的慢响应</span></span><br><span class="line">		fmt.Fprintf(w, &amp;quot;slow response&amp;quot;)</span><br><span class="line">		<span class="keyword">return</span></span><br><span class="line">	&#125;</span><br><span class="line">	fmt.Fprint(w, &amp;quot;quick response&amp;quot;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	http.HandleFunc(&amp;quot;/&amp;quot;, indexHandler)</span><br><span class="line">	err := http.ListenAndServe(&amp;quot;:<span class="number">8000</span>&amp;quot;, <span class="literal">nil</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(err)</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="client端"><a href="#client端" class="headerlink" title="client端"></a>client端</h1><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// context_timeout/client/main.go</span></span><br><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	&amp;quot;context&amp;quot;</span><br><span class="line">	&amp;quot;fmt&amp;quot;</span><br><span class="line">	&amp;quot;io/ioutil&amp;quot;</span><br><span class="line">	&amp;quot;net/http&amp;quot;</span><br><span class="line">	&amp;quot;sync&amp;quot;</span><br><span class="line">	&amp;quot;time&amp;quot;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 客户端</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> respData <span class="keyword">struct</span> &#123;</span><br><span class="line">	resp *http.Response</span><br><span class="line">	err  error</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">doCall</span><span class="params">(ctx context.Context)</span></span> &#123;</span><br><span class="line">	transport := http.Transport&#123;</span><br><span class="line">	   <span class="comment">// 请求频繁可定义全局的client对象并启用长链接</span></span><br><span class="line">	   <span class="comment">// 请求不频繁使用短链接</span></span><br><span class="line">	   DisableKeepAlives: <span class="literal">true</span>, 	&#125;</span><br><span class="line">	client := http.Client&#123;</span><br><span class="line">		Transport: &amp;amp;transport,</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	respChan := <span class="built_in">make</span>(<span class="keyword">chan</span> *respData, <span class="number">1</span>)</span><br><span class="line">	req, err := http.NewRequest(&amp;quot;GET&amp;quot;, &amp;quot;http:<span class="comment">//127.0.0.1:8000/&amp;quot;, nil)</span></span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		fmt.Printf(&amp;quot;<span class="built_in">new</span> requestg failed, err:%v\n&amp;quot;, err)</span><br><span class="line">		<span class="keyword">return</span></span><br><span class="line">	&#125;</span><br><span class="line">	req = req.WithContext(ctx) <span class="comment">// 使用带超时的ctx创建一个新的client request</span></span><br><span class="line">	<span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line">	wg.Add(<span class="number">1</span>)</span><br><span class="line">	<span class="keyword">defer</span> wg.Wait()</span><br><span class="line">	<span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">		resp, err := client.Do(req)</span><br><span class="line">		fmt.Printf(&amp;quot;client.do resp:%v, err:%v\n&amp;quot;, resp, err)</span><br><span class="line">		rd := &amp;amp;respData&#123;</span><br><span class="line">			resp: resp,</span><br><span class="line">			err:  err,</span><br><span class="line">		&#125;</span><br><span class="line">		respChan &amp;lt;- rd</span><br><span class="line">		wg.Done()</span><br><span class="line">	&#125;()</span><br><span class="line"></span><br><span class="line">	<span class="keyword">select</span> &#123;</span><br><span class="line">	<span class="keyword">case</span> &amp;lt;-ctx.Done():</span><br><span class="line">		<span class="comment">//transport.CancelRequest(req)</span></span><br><span class="line">		fmt.Println(&amp;quot;call api timeout&amp;quot;)</span><br><span class="line">	<span class="keyword">case</span> result := &amp;lt;-respChan:</span><br><span class="line">		fmt.Println(&amp;quot;call server api success&amp;quot;)</span><br><span class="line">		<span class="keyword">if</span> result.err != <span class="literal">nil</span> &#123;</span><br><span class="line">			fmt.Printf(&amp;quot;call server api failed, err:%v\n&amp;quot;, result.err)</span><br><span class="line">			<span class="keyword">return</span></span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">defer</span> result.resp.Body.Close()</span><br><span class="line">		data, _ := ioutil.ReadAll(result.resp.Body)</span><br><span class="line">		fmt.Printf(&amp;quot;resp:%v\n&amp;quot;, <span class="keyword">string</span>(data))</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="comment">// 定义一个100毫秒的超时</span></span><br><span class="line">	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*<span class="number">100</span>)</span><br><span class="line">	<span class="keyword">defer</span> cancel() <span class="comment">// 调用cancel释放子goroutine资源</span></span><br><span class="line">	doCall(ctx)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></section>
    <!-- Tags START -->
    
      <div class="tags">
        <span>Tags:</span>
        
  <a href="/tags#Go" >
    <span class="tag-code">Go</span>
  </a>

      </div>
    
    <!-- Tags END -->
    <!-- NAV START -->
    
  <div class="nav-container">
    <!-- reverse left and right to put prev and next in a more logic postition -->
    
      <a class="nav-left" href="/go/2-Go%E8%AF%AD%E8%A8%80%E5%9F%BA%E7%A1%80/21-Go%E5%8D%8F%E7%A8%8B/">
        <span class="nav-arrow">← </span>
        
          21-Go 协程
        
      </a>
    
    
      <a class="nav-right" href="/go/2-Go%E8%AF%AD%E8%A8%80%E5%9F%BA%E7%A1%80/25-Mutex/">
        
          25-Mutex
        
        <span class="nav-arrow"> →</span>
      </a>
    
  </div>

    <!-- NAV END -->
    <!-- 打赏 START -->
    
      <div class="money-like">
        <div class="reward-btn">
          赏
          <span class="money-code">
            <span class="alipay-code">
              <div class="code-image"></div>
              <b>使用支付宝打赏</b>
            </span>
            <span class="wechat-code">
              <div class="code-image"></div>
              <b>使用微信打赏</b>
            </span>
          </span>
        </div>
        <p class="notice">点击上方按钮,请我喝杯咖啡！</p>
      </div>
    
    <!-- 打赏 END -->
    <!-- 二维码 START -->
    
      <div class="qrcode">
        <canvas id="share-qrcode"></canvas>
        <p class="notice">扫描二维码，分享此文章</p>
      </div>
    
    <!-- 二维码 END -->
    
      <!-- Gitment START -->
      <div id="comments"></div>
      <!-- Gitment END -->
    
  </article>
  <!-- Article END -->
  <!-- Catalog START -->
  
    <aside class="catalog-container">
  <div class="toc-main">
  <!-- 不蒜子统计 -->
    <strong class="toc-title">目录</strong>
    
      <ol class="toc-nav"><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#为什么需要Context"><span class="toc-nav-text">为什么需要Context</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#基本示例"><span class="toc-nav-text">基本示例</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#全局变量方式"><span class="toc-nav-text">全局变量方式</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#通道方式"><span class="toc-nav-text">通道方式</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#官方版的方案"><span class="toc-nav-text">官方版的方案</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#Context初识"><span class="toc-nav-text">Context初识</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#Context接口"><span class="toc-nav-text">Context接口</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#Background-和TODO"><span class="toc-nav-text">Background()和TODO()</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#With系列函数"><span class="toc-nav-text">With系列函数</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#WithCancel"><span class="toc-nav-text">WithCancel</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#WithDeadline"><span class="toc-nav-text">WithDeadline</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#WithTimeout"><span class="toc-nav-text">WithTimeout</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#WithValue"><span class="toc-nav-text">WithValue</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#使用Context的注意事项"><span class="toc-nav-text">使用Context的注意事项</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#客户端超时取消示例"><span class="toc-nav-text">客户端超时取消示例</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#server端"><span class="toc-nav-text">server端</span></a></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#client端"><span class="toc-nav-text">client端</span></a></li></ol>
    
  </div>
</aside>
  
  <!-- Catalog END -->
</main>

<script>
  (function () {
    var url = 'http://www.liuqingzheng.top/go/3-Go语言常用标准库/01-Go语言之context/';
    var banner = ''
    if (banner !== '' && banner !== 'undefined' && banner !== 'null') {
      $('#article-banner').css({
        'background-image': 'url(' + banner + ')'
      })
    } else {
      $('#article-banner').geopattern(url)
    }
    $('.header').removeClass('fixed-header')

    // error image
    $(".markdown-content img").on('error', function() {
      $(this).attr('src', 'http://file.muyutech.com/error-img.png')
      $(this).css({
        'cursor': 'default'
      })
    })

    // zoom image
    $(".markdown-content img").on('click', function() {
      var src = $(this).attr('src')
      if (src !== 'http://file.muyutech.com/error-img.png') {
        var imageW = $(this).width()
        var imageH = $(this).height()

        var zoom = ($(window).width() * 0.95 / imageW).toFixed(2)
        zoom = zoom < 1 ? 1 : zoom
        zoom = zoom > 2 ? 2 : zoom
        var transY = (($(window).height() - imageH) / 2).toFixed(2)

        $('body').append('<div class="image-view-wrap"><div class="image-view-inner"><img src="'+ src +'" /></div></div>')
        $('.image-view-wrap').addClass('wrap-active')
        $('.image-view-wrap img').css({
          'width': `${imageW}`,
          'transform': `translate3d(0, ${transY}px, 0) scale3d(${zoom}, ${zoom}, 1)`
        })
        $('html').css('overflow', 'hidden')

        $('.image-view-wrap').on('click', function() {
          $(this).remove()
          $('html').attr('style', '')
        })
      }
    })
  })();
</script>


  <script>
    var qr = new QRious({
      element: document.getElementById('share-qrcode'),
      value: document.location.href
    });
  </script>



  <script>
    var gitmentConfig = "liuqingzheng";
    if (gitmentConfig !== 'undefined') {
      var gitment = new Gitment({
        id: "Go语言之context",
        owner: "liuqingzheng",
        repo: "FuckBlog",
        oauth: {
          client_id: "32a4076431cf39d0ecea",
          client_secret: "94484bd79b3346a949acb2fda3c8a76ce16990c6"
        },
        theme: {
          render(state, instance) {
            const container = document.createElement('div')
            container.lang = "en-US"
            container.className = 'gitment-container gitment-root-container'
            container.appendChild(instance.renderHeader(state, instance))
            container.appendChild(instance.renderEditor(state, instance))
            container.appendChild(instance.renderComments(state, instance))
            container.appendChild(instance.renderFooter(state, instance))
            return container;
          }
        }
      })
      gitment.render(document.getElementById('comments'))
    }
  </script>




    <div class="scroll-top">
  <span class="arrow-icon"></span>
</div>
    <footer class="app-footer">
<!-- 不蒜子统计 -->
<span id="busuanzi_container_site_pv">
     本站总访问量<span id="busuanzi_value_site_pv"></span>次
</span>
<span class="post-meta-divider">|</span>
<span id="busuanzi_container_site_uv" style='display:none'>
     本站访客数<span id="busuanzi_value_site_uv"></span>人
</span>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>



  <p class="copyright">
    &copy; 2021 | Proudly powered by <a href="https://www.cnblogs.com/xiaoyuanqujing" target="_blank">小猿取经</a>
    <br>
    Theme by <a href="https://www.cnblogs.com/xiaoyuanqujing" target="_blank" rel="noopener">小猿取经</a>
  </p>
</footer>

<script>
  function async(u, c) {
    var d = document, t = 'script',
      o = d.createElement(t),
      s = d.getElementsByTagName(t)[0];
    o.src = u;
    if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
    s.parentNode.insertBefore(o, s);
  }
</script>
<script>
  async("//cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.6/fastclick.min.js", function(){
    FastClick.attach(document.body);
  })
</script>

<script>
  var hasLine = 'true';
  async("//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js", function(){
    $('figure pre').each(function(i, block) {
      var figure = $(this).parents('figure');
      if (hasLine === 'false') {
        figure.find('.gutter').hide();
      }
      var lang = figure.attr('class').split(' ')[1] || 'code';
      var codeHtml = $(this).html();
      var codeTag = document.createElement('code');
      codeTag.className = lang;
      codeTag.innerHTML = codeHtml;
      $(this).attr('class', '').empty().html(codeTag);
      figure.attr('data-lang', lang.toUpperCase());
      hljs.highlightBlock(block);
    });
  })
</script>





<!-- Baidu Tongji -->

<script>
    var _baId = 'c5fd96eee1193585be191f318c3fa725';
    // Originial
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "//hm.baidu.com/hm.js?" + _baId;
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
</script>


<script src="/js/script.js"></script>


<script src="/js/search.js"></script>


<script src="/js/load.js"></script>



  <span class="local-search local-search-google local-search-plugin" style="right: 50px;top: 70px;;position:absolute;z-index:2;">
      <input type="search" placeholder="站内搜索" id="local-search-input" class="local-search-input-cls" style="">
      <div id="local-search-result" class="local-search-result-cls"></div>
  </span>


  </body>
</html>